/**
 ******************************************************************************
 * @file	fpga-2016.h
 * @brief	fpga-2016 存储器通信

采用原始 fpga-2016 SPI通信协议


 *
 @section Platform
	-#
 @section Library
	-#
 *
 * @section Depend
 *	-# spi_mxs.ko
 *	-# spidev.ko

- 2016-12-14,MenglongWu,MenglongWoo@aliyun.com
*/

#ifndef _FPGA_2016_H_
#define _FPGA_2016_H_
#include <autoconfig.h>
#include <stdbool.h>
#include <pthread.h>

#define BIT_DEF 0x01
#define BIT_TX 0x02
#define BIT_RX 0x04
#define BIT_OTDR_DATA 0x08
#if CONFIG_USE_PRINTL
	#define OTDR_DBG_LEVEL (LEVEL_ID(LEVEL_GROUP - 1,SUB_GROUP - 1))
	#define DBG_TX (OTDR_DBG_LEVEL & BIT_TX)
	#define DBG_RX (OTDR_DBG_LEVEL & BIT_RX)
	#define DBG_BIT_OTDR_DATA (OTDR_DBG_LEVEL & BIT_OTDR_DATA)

	#define EN_OTDR_DBG_LEVEL(en) (OTDR_DBG_LEVEL |= (en))
	#define DISABLE_OTDR_DBG_LEVEL(en) (PRINT_OTDR_DATA &= ~(en))
#else
	#define OTDR_DBG_LEVEL 1
	#define PRINT_OTDR_DATA 1

	#define DBG_TX 1
	#define DBG_RX 1
	#define DBG_BIT_OTDR_DATA 1

	#define EN_OTDR_DBG_LEVEL(en)
	#define EN_PRINT_OTDR_DATA(en)
	#define EN_OTDR_PRINTL(en)
#endif


/** @defgroup fpga_interface
  * @{
  */

/**
 * @brief	fpga 设备描述
 */
struct fpga_dev {
	int fd;					///< 设备FD，正常 fpga_open 后返回
	unsigned long pid;			///< 设备id，正常 fpga_open 后返回
	char *desc;				///< 设备描述，，正常 fpga_open 后返回
	unsigned long checksum;			///< OTDR 曲线数据校验值，fpga_otdr_isfinish返回
	unsigned long mask_amp;			///< amp 有效值掩码，fpga_test_param.hw_amp的取值范围
	unsigned long mask_apd;			///< apd 有效值掩码，fpga_test_param.hw_apd_vol的取值范围
	pthread_mutex_t mutex;			///< 互斥锁
	// void *private;

	// 如下内容放入private
	unsigned long sample_freq;	///< 采样频率选择，单位hz
	unsigned long adopt_pt;	///< 采样点数，单位个
	unsigned char ch;
};
struct fpga_info {
	int hw_ver;  ///< 硬件版本
	int ch;		///< 通道数
	int soft_ver;	///< 软件版本
};
static inline unsigned long get_sample_rate(struct fpga_dev *dev)
{
	return (dev->sample_freq);
} 	
static inline unsigned long get_sample_num(struct fpga_dev *dev)
{
	return (dev->adopt_pt);
} 	
static inline unsigned long get_ch(struct fpga_dev *dev)
{
	return (dev->ch);
} 	
/**
 * @brief	otdr 测试参数
 * @remark	可以通过 @ref 构建默认测试参数 填充该项
 */
struct fpga_test_param {
	/*!<
		OTDR 测试参数描述，与硬件无关

	           <      1 / laser_freq   >
	           <  pulse >
	            --------                 --------
	           |        |               |        |
	           |        |               |        |
	           |        |               |        |
	        ---|        |---------------|        |------------

	            ^ ^ ^ ^ ^ ^ ^ ^          ^ ^ ^ ^ ^ ^ ^ ^
	            | | | | | | | |          | | | | | | | |
	                   /   \
	                  /     \
	                /         \
	              /             \
	             <1 / sample_freq>
	*/
	bool en_hw_dist;
	unsigned long dist;		/// 测试量程单位米，该字段内容决定采用什么采样率 fpga_test_param.sample_freq

	unsigned short laser_freq;	///< 激光器脉冲频率选择
	bool en_hw_pulse;
	unsigned short pulse;		/*!< 脉冲宽度，单位ns*/


	unsigned short sample_freq;	///< 采样频率选择，单位hz

	bool en_hw_adopt_pt;
	unsigned long  adopt_pt;	///< 采样点数，单位个
	bool en_hw_add_cnt;
	unsigned short add_cnt;		///< 累加次数，单位次
	bool en_hw_add_ms;
	unsigned short add_ms;	 	///< 累加时间，单位ms

	unsigned long amp;		/*!< 使能放大： 1：使能放大开关，与具体放大倍数无关.
					但必须保证，各放大开关打开之后效果必定比前一个大\n

					运放级别分成3级，对应24bit \n
					 00000000 00000000 00000000 00000000\n
					|..........................|=======| ----  1级放大\n
					|.................|=======|......... ----  2级放大\n
					|........|=======|.................. ----  3级放大\n
					|=======|...........................  ----  预留\n
					 例子:\n\n
					 00000000 00000000 00000011 00000001\n ---> 使能1级放大开关S1-1，使能2级放大S2-1、S2-2
					*/
	bool en_hw_amp;
	char hw_amp;		

	char apd_vol;			/*!< APD电压，数值越高电压越高，@ref APD电压范围
					OTDR_APD_LOW~OTDR_APD_HIGH
	  				*/
	bool en_hw_apd_vol;
	char hw_apd_vol;

	char power;			/*!< 激光器功率，数值越高功率越高，@ref 光功率控制
					OTDR_LASER_PW_LOW ~ OTDR_LASER_PW_HIGH
					*/

	char isrun;			///< 是否启动


	short laser_ch;			/*!< 激光器通道，从1开始计数，
					同时 “可能选择性” 支持以波长（单位nm）方式指定通道，
					如：1310、1550、1625。\n
					1 通道对应 1310nm 激光器\n
					2 通道对应 1550nm 激光器\n
					具体对应关系与接口实现有关
					*/
	char panel_ch;			/*!< 面板光口通道，从 1 开始计数
					可能由光开关控制，同一面板通道可能不同时刻用于不同激光器通道
					*/
};


/** @defgroup 各量程脉冲列表
* @{
*
* 例如TP_PULSE_100KM_5120_NS与TP_PULSE_180KM_10240_NS对应的值均是128，但生成的
* 脉冲宽度不同，本质在于两100Km量程下采样始终25MHz，180Km量程12MHz

<table>
<tr><th>量程(km) <th>采样频率(MHz) 	<th>采样时间(ms)	<th>光往返时间(ms)	<th>脉冲周期(ms)	<th>1s内单个时钟对应的累加次数
<tr><td>1	<td>  800		<td>0.04		<td>0.01		<td>0.1 		<td>10000
<tr><td>5	<td>  400		<td>0.08		<td>0.05		<td>0.3125 		<td>3200
<tr><td>10	<td>  200		<td>0.16		<td>0.1			<td>0.5			<td>2000
<tr><td>30	<td>  100		<td>0.32		<td>0.3			<td>1 			<td>1000
<tr><td>60	<td>  50		<td>0.64		<td>0.6			<td>1			<td>1000
<tr><td>100	<td>  25		<td>1.28		<td>1			<td>2.666 		<td>375
<tr><td>200	<td>  12.5		<td>2.56		<td>2			<td>2.666		<td>375
</table>
*/
/** end 各量程脉冲列表
 * @}
 */







/** @defgroup FPGA导出接口
  * @{
  */


/**
 * @brief	打开OTDR采集卡，设备挂在SPI总线上
 * @param[in]	device 设备节点
 * @param[out]	dev 设备喵师傅
 * @param[in]	ispeed 指定通信速率，传递0时采用默认500KHz
 * @retval	0  成功 dev返回有效值
 * @retval	-1 设备节点打开失败
 * @retval	-2 设备打开成功，但检测不到FPGA
 * @see fpga_close
 */
int fpga_open(char *device, struct fpga_dev **dev, unsigned long ispeed);

/**
 * @brief	关闭已打开的otdr采集卡
 * @param[in]	dev 设备描述符
 * @retval	null
 * @see fpga_open
 */
int  fpga_close(struct fpga_dev *dev);

/**
 * @brief	otdr测试开始
 * @param[in]	dev 设备描述符
 * @retval	null
 * @remarks	之前必须先保证调用 @ref fpga_otdr_param 配置有效值，如果参数每变化
 	之后可连续调用 	fpga_otdr_start
 * @see	fpga_otdr_param
 */
int fpga_otdr_start(struct fpga_dev *dev);
/**
 * @brief	无论当前测试是否已经启动，停止OTDR测试
 * @param[in]	dev 设备描述符
 * @remarks
 * @see
 */

int fpga_otdr_stop(struct fpga_dev *dev);
/**
 * @brief	询问OTDR是否已经完成一组测试，完成则记录数据的校验值，
 		校验值存储在 fpga_dev.checksum 应用程序无需处理。\n
 		完成时间长短由 @ref fpga_otdr_param
 		配置参数 fpga_test_param.adopt_pt ， fpga_test_param.add_cnt 决定，
 		数值越大完成一组测试时间越久
 * @param[in]			dev 设备描述符
 * @param[in][out]		isfinish 完成状态
 * @retval	0 通信正常
 	isfinish=true 准备完毕
 	isfinish=false 忙碌
 * @retval	-1 通信错误
 * @remarks	保证之前已经启动测试 @ref fpga_otdr_start，在无测试情况下永远返回完成
 * @see fpga_otdr_start
 */

int fpga_otdr_isfinish(struct fpga_dev *dev, bool *isfinish);





/** @defgroup 运放控制
* @{
*/

#define TP_AMP_L1(n) ((n) & 0xff)		///< 运放1级放大  参考 struct fpga_test_param.amp
#define TP_AMP_L2(n) (((n) & 0xff) << 8)	///< 运放2级放大  参考 struct fpga_test_param.amp
#define TP_AMP_L3(n) (((n) & 0xff) << 16)	///< 运放3级放大  参考 struct fpga_test_param.amp
#define TP_AMP_L4(n) (((n) & 0xff) << 24)	///< 运放4级放大 参考 struct fpga_test_param.amp
/** end 运放控制
 * @}
 */


/** @defgroup APD电压范围
 * @{
 */
#define TP_APD_39V	(39)///< APD 39V
#define TP_APD_30V	(30)///< APD 30V
#define OTDR_APD_HIGH	TP_APD_39V
#define OTDR_APD_LOW	TP_APD_30V
/** end APD电压范围
 * @}
 */


/** @defgroup 光功率控制
 * @{
 */
#define OTDR_LASER_PW_LV1		1 ///< 激光器功率 Level1 最小功率
#define OTDR_LASER_PW_LV2		2 ///< 激光器功率 Level2 
#define OTDR_LASER_PW_LV3		3 ///< 激光器功率 Level3 
#define OTDR_LASER_PW_LV4		4 ///< 激光器功率 Level4 最大功率
#define OTDR_LASER_PW_HIGH		OTDR_LASER_PW_LV4 ///< 激光器功率 最大功率
#define OTDR_LASER_PW_LOW		OTDR_LASER_PW_LV1 ///< 激光器功率 最大功率
/** end 光功率控制
 * @}
 */


/**
 * @brief	设置OTDR测试参数，设置之后应该有10ms延时，等待光耦开关稳定
 		才能执行 @ref fpga_otdr_start 测试，如果两次调用 fpga_otdr_param 
 		之间没有调用 fpga_exist_com_laser 则不延时
 * @param[in]	dev 设备描述符
 * @param[in]	val 所有参数采用自然编码，具体参数意义参考 struct fpga_test_param
 * @retval	null
 * @remarks @ref fpga_exist_com_laser 无视当前测试参数，自动切换测试量程，
 	所以每次检查通信光状态后都应该重新设置测试参数
 * @see fpga_otdr_start
 * @see fpga_exist_com_laser 
 * @see	_encode_laser  @see _encode_adopt  @see _encode_add_cnt
 * @see _encode_power  @see _encode_amp  @see _encode_apd_vol
 * @see _encode_slot  @see _encode_ch
 */
int fpga_otdr_param(struct fpga_dev *dev, struct fpga_test_param *val);
/**
 * @brief	请求测试并等待一组测试数据准备完好后返回
 		等同于 @ref fpga_otdr_param ~ @ref fpga_otdr_isfinish 之间的所有调用
 * @param[in]	dev 设备描述符
 * @param[in]	val 测试参数
 *		默认各量程测试参数填充 @ref 构建默认测试参数 可在默认基础上做修改
 * @retval	0 数据准备完好
 * @retval	-1 通信失败或等待数据超时间
 * @see fpga_otdr_param
 * @see fpga_otdr_isfinish
 */
int fpga_request_test(struct fpga_dev *dev, struct fpga_test_param *val);



/** @defgroup 红光模式
  * @{
  */
#define OTDR_RL_OFF   (0)		///< 关闭红光
#define OTDR_RL_ON    (1)		///< 红光常亮 1.2ms高电平，3.7ms低电平
#define OTDR_RL_1HZ   (2)
#define OTDR_RL_10HZ  (3)
#define OTDR_RL_SLOW  OTDR_RL_1HZ	///< 红光慢闪烁 1HZ 500ms高电平，500ms低电平
#define OTDR_RL_FAST  OTDR_RL_10HZ	///< 红光快闪烁 10HZ 100ms高电平，100ms低电平
/** end 红光模式
 * @}
 */

/**
 * @brief	红光输出控制
 * @param[in]	dev 设备描述符
 * @param[in]	val 红光模式
 *		模式选择范围 @ref 红光模式
 * @retval	null
 */
int fpga_red_laser(struct fpga_dev *dev, int mode);

/**
 * brief	调节LCD背光
 * @param[in]	dev 设备描述符
 * @param[in]	val lcd 背光输出频率
 * @retval	null
 * @remarks
 * @see
 */
int fpga_lcd_bl(struct fpga_dev *dev, int val);

/**
 * @brief	获取电池状态
 * @param[in]	dev 设备描述符
 * @param[in]	status 状态
 * @retval	null
 * @remarks
 * @see
 */
int fpga_charge_status(struct fpga_dev *dev, char *status);

/** @defgroup 低功耗模式
  * @{
  */
#define OTDR_LOW_POWER (0)	/*!< 低功耗 */
#define OTDR_OFF_POWER (1)	/*!< 关闭电源，此电源是处理器总电源，如果存在ARM与FPGA同时控制该电源，
                                     则先FPGA关断，后ARM关断*/
/** end 低功耗模式
 * @}
 */

/**
 * @brief	fpga电源模式
 * @param[in]	dev 设备描述符
 * @param[out]	mode 选择电源模式
 *		模式选择范围 @ref 低功耗模式
 * @retval	null
 * @remarks	该命令FPGA没有返回值，永远认为成功
 * 		当选择关闭电源模式OTDR_OFF_POWER时，处理器端必须保证其数据安全保存
 */
int fpga_low_power(struct fpga_dev *dev, int mode);

/**
 * @brief	通信光检测
 * @param[in]	dev 设备描述符
 * @param[out]	status 光路通信状态
 * @retval	0 通信正常
 	isexist=true 存在通信光
 	isexist=false 不存在通信光
 * @retval	-1 通信错误
 * @remarks
 */
int fpga_exist_com_laser(struct fpga_dev *dev, bool *isexist);

/**
 * @brief	读取FPGA软硬件版本号
 * @param[in]	dev 设备描述符
 * @param[out]	soft_ver 表示该软件所能支持的硬件版本
 * @param[out]	hw_ver 硬件外围器件特性，可随时根据生产而修改，读取的是电路板上
 		拨码开关或固定电阻
 * @retval	0 通信正常
 * @retval	-1 通信错误
 * @remarks	有待定义软硬件版本具体描述
 */

int fpga_version(struct fpga_dev *dev,  char *soft_ver, char *hw_ver);
int fpga_info(struct fpga_dev *dev,  struct fpga_info *out);


/**
 * @brief	读取OTDR所有数据，并计算校验值是否匹配\n
 		向FPGA发送读取指令，@ref _read_otdr_first \n
 		若干次调用 @ref _read_otdr_serial 读取余下若干帧
 * @param	otdr_data 存储OTDR数据点，每个点以4byte存储
 * @param	len otdr_data 存储长度，必须是 32000* 4、64000*4、128000*4之一
 * @retval 0 通信正常校验值比对成功
 * @retval -1 通信正常校验值比对失败，通信错误
 * @remarks	读取前必须保证数据已经准备好 @ref fpga_otdr_isfinish ，
 	发送方的正确校验码在 fpga_otdr_isfinish 里传输，无论校验正确与否  
 	otda_data 都被修改
 * @bug 1. 整个 fpga_read_otdr_data 必须独占总线，未完成前总线被其他对象占用
 	都讲导致数据错误（该缺陷由通信协议确定）

 * @bug 2. 如果读取曲线发生校验码错误，则必须重新测试
	（fpga_otdr_start - fpga_otdr_isfinish - fpga_read_otdr_data）。
	在不重测情况下，多次调用fpga_read_otdr_data永远校验错误。
 * @see	fpga_otdr_isfinish
 * @see _otdr_data_swap
 * @see _otdr_checksum
 * @see _read_otdr_first
 * @see _read_otdr_serial
 */

int fpga_read_otdr_data(
    struct fpga_dev *dev,
    unsigned char *otdr_data,
    unsigned long len);

/**
 * @brief	调试光耦开关，被选定的开关变成高电平，“同功能开关”的其余开关变成低电平，
 		其余开关的状态是它收到控制器发送 00 的结果，具体开关高电平有效还是
 		低电平有效由硬件而定。\n
 	何为同功能开关：\n
		amp 与 apd 都是用光耦开关控制，各有若干开关。若：\n
		amp有6个开关，分别对应S1-S6，属于同功能开关\n
		apd有3个开关，分别对应S7-S9，属于另一同功能开关\n
		当sw_index=1 时，表示 S1 为高电平，S2-S6为低电平，至于S7-S9状态不做控制，
		同理设置sw_index=7时, 表示 S7 为高电平，S8-S9为低电平，至于S1-S6状态不做控制，

 * @param[in]	dev 设备描述符
 * @param[in]	param 所有参数采用自然编码，具体参数意义参考 struct fpga_test_param
 * @param[in]	sw_index 唯一使能的光耦开关，使能后高电平
 * @retval 0 通信正常校验值比对成功
 * @retval -1 通信正常校验值比对失败，通信错误
 */
unsigned long fpga_dbg_sw(struct fpga_dev *dev, struct fpga_test_param *param, int sw_index);

/**
 * @brief	检测FPGA是否存在，心跳
 * @param[in]	dev 设备描述符
 * @retval	0 存在
 * @retval	-1 不存在，或总线错误
 * @remarks	fpga_open 里自动调用它
 */

unsigned int fpga_alive(struct fpga_dev *dev);

/** @defgroup 构建默认测试参数
 * @{
 *
 * 所谓的自动配置并不是真的自动配置，而是因为采样时钟测量距离、测量脉宽之间
 * 关系密切，所以启动一个测试量程测试前先用对应的 AUTO_BUILD_xxx 构造结构体
 * struct fpga_test_param 模板，然后再根据需要做细微修改，如激光器(laser)、运放
 * (amp)、APD电压(apd)等
 *
 * @see 各量程脉冲列表
 */
#define AUTO_BUILD_300M(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 20/*TP_PULSE_1KM_40_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV1 ;\
		p->isrun    = 1; \
	} ///< 默认 300M 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_1KM(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 20/*TP_PULSE_5KM_160_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV2 ;\
		p->isrun    = 1; \
	}///< 默认 1KM 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_5KM(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 80/*TP_PULSE_5KM_160_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV2;\
		p->isrun    = 1; \
	} ///< 默认 5KM 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_10KM(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 80/*TP_PULSE_10KM_320_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV2;\
		p->isrun    = 1; \
	} ///< 默认 10KM 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_60KM(ptr) \
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 80/*TP_PULSE_60KM_1280_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV3;\
		p->isrun    = 1; \
	}	///< 默认 60KM 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_100KM(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 80/*TP_PULSE_100KM_10240_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV3;\
		p->isrun    = 1; \
	} ///< 默认 100KM 配置配置参数，填充 fpga_test_param
#define AUTO_BUILD_180KM(ptr)	\
	{ \
		struct fpga_test_param *p = (struct fpga_test_param *)ptr; \
		p->laser_ch    = 1310;\
		p->pulse    = 80/*TP_PULSE_180KM_20480_NS*/ ;\
		p->sample_freq     = 0 ;\
		p->adopt_pt = 32000 ;\
		p->add_cnt  = 10000 ;\
		p->amp      = TP_AMP_L1(0x01);\
		p->apd_vol  = TP_APD_39V ;\
		p->power    = OTDR_LASER_PW_LV4;\
		p->isrun    = 1; \
	} ///< 默认 200KM 配置配置参数，填充 fpga_test_param


/** end 构建默认测试参数
* @}
*/

/** end FPGA导出接口
 * @}
 */


/** end fpga_interface
 * @}
 */
#endif

